 /*
  * The contents of this file are subject to the Mozilla Public License
  * Version 1.1 (the "License");  you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  * http//www.mozilla.org/MPL/
  *
  * Software distributed under the License is distributed on an "AS IS" basis,
  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
  * the specific language governing rights and limitations under the License.
  *
  * The Original Code is ST-EXT-SemanticAnnotation.
  *
  * The Initial Developer of the Original Code is University of Roma Tor Vergata.
  * Portions created by University of Roma Tor Vergata are Copyright (C) 2008.
  * All Rights Reserved.
  *
  * ST-EXT-SemanticAnnotation was developed by the Artificial Intelligence Research Group
  * (ai-nlp.info.uniroma2.it) at the University of Roma Tor Vergata
  * Current information about ST-EXT-SemanticAnnotation can be obtained at 
  * http//ai-nlp.info.uniroma2.it/software/...
  *
  */

  /*
   * Contributor(s): Armando Stellato stellato@info.uniroma2.it
  */
package it.uniroma2.art.semannot.servlet;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;

import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.iterators.FilterIterator;
import org.apache.log4j.Logger;
import org.apache.xerces.dom.DocumentImpl;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import it.uniroma2.art.ontapi.ARTLiteral;
import it.uniroma2.art.ontapi.ARTRepository;
import it.uniroma2.art.ontapi.ARTResource;
import it.uniroma2.art.ontapi.RepositoryUtilities;
import it.uniroma2.art.ontapi.exceptions.RepositoryUpdateException;
import it.uniroma2.art.ontapi.filter.ResourceOfATypePredicate;
import it.uniroma2.art.ontapi.utilities.DeletePropagationPropertyTree;
import it.uniroma2.art.ontapi.vocabulary.RDF;
import it.uniroma2.art.semanticturkey.SemanticTurkey;
import it.uniroma2.art.semanticturkey.SemanticTurkeyOperations;
import it.uniroma2.art.semanticturkey.plugin.extpts.CXSL;
import it.uniroma2.art.semanticturkey.plugin.extpts.InterfaceServiceServlet;
import it.uniroma2.art.semanticturkey.resources.Resources;
import it.uniroma2.art.semanticturkey.servlet.ServletUtilities;
import it.uniroma2.art.semanticturkey.utilities.XMLHelp;
import it.uniroma2.art.semanticturkey.vocabulary.SemAnnotVocab;

public class RangeAnnotation extends InterfaceServiceServlet {
    protected final String urlPageString = "urlPage";
    protected final String titleString = "title";
    protected final String annotQNameString = "annotQName";
    protected final String subjectInstanceNameString = "subjectInstanceName";
    protected final String textField = "text";
    protected final String rangeField = "range";
    protected final Logger s_logger = Logger.getLogger(SemanticTurkey.class);
    protected final String clsQNameField = "clsQName";
    protected final String instanceQNameField = "instanceQName";
    
    protected ServletUtilities servletUtilities;    
    protected DeletePropagationPropertyTree deletePropertyPropagationTreeForAnnotations;
    
    public RangeAnnotation(String id) {
        this.id = id;
        servletUtilities = new ServletUtilities();
    }

    protected void initializeDeletePropertyPropagationTreeForAnnotations() {
        deletePropertyPropagationTreeForAnnotations = new DeletePropagationPropertyTree();
        deletePropertyPropagationTreeForAnnotations.addChild(SemAnnotVocab.Res.location);
    }
    
    
    public Document XMLData() {     
        Document xml=null;
        String request = _oReq.getParameter("request");
        if (request==null)
            request="";   //TODO remove this! put just for capturing old "without request parameter" calls
        
        if (request.equals("getPageAnnotations") ) {
            String urlPage = _oReq.getParameter(urlPageString);
            xml = getPageAnnotations(urlPage);
        }
        else if (request.equals("chkAnnotations")) {
            String urlPage = _oReq.getParameter(urlPageString);
            xml = chkPageForAnnotations(urlPage);
        }       
        else if (request.equals("removeAnnotation")) {
            String annotQName = _oReq.getParameter(annotQNameString);
            xml = removeAnnotation(annotQName);
        }           
        else if (request.equals("createAndAnnotate")) {
            
            String clsQName = servletUtilities.removeInstNumberParentheses(_oReq.getParameter(clsQNameField));          
            String instanceNameEncoded = servletUtilities.encodeLabel(_oReq.getParameter(instanceQNameField));
                        
            String urlPage = _oReq.getParameter(urlPageString);         
            String title = _oReq.getParameter(titleString);
            String range = _oReq.getParameter(rangeField);
            
            xml = dragDropSelectionOverClass(instanceNameEncoded, clsQName, urlPage, title, range); 

        }  
        else if (request.equals("addAnnotation")) {         
            String urlPage = _oReq.getParameter(urlPageString);           
            String subjectInstanceName = _oReq.getParameter(subjectInstanceNameString);
            String text = _oReq.getParameter(textField);
            String textEncoded = servletUtilities.encodeLabel(text);
            String title = _oReq.getParameter(titleString);
            String range = _oReq.getParameter(rangeField);
            annotateInstanceWithDragAndDrop(subjectInstanceName, textEncoded, urlPage, title, range);
            //NScarpato 25/06/2007 add ack
            Element tree = xml.createElement("Tree");
            tree.setAttribute("type","Ack");
            xml.appendChild(tree);
        }       
        else if (request.equals("saveAnnotations")) {
            String outPutFile = _oReq.getParameter("file");
            return saveAnnotations(new File(outPutFile));
        }/* TODO add relateandAnnotate service for range annotator
         else if (request.equals("relateAndAnnotate")) {
            String subjectInstanceQName = _oReq.getParameter(instanceQNameField);   
            String predicatePropertyName = _oReq.getParameter(propertyQNameField);      
            String objectInstanceName = _oReq.getParameter(objectQNameField);
            String objectInstanceNameEncoded = servletUtilities.encodeLabel(objectInstanceName);
            String urlPage = _oReq.getParameter(urlPageField);          
            String title = _oReq.getParameter(titleField);
            String op = _oReq.getParameter("op");        

            //httpGet2( http://127.0.0.1:1979/semantic_turkey/resources/stserver/STServer?service=property&instanceQName=Armando Stellato&propertyQName=filas:worksInCompany&objectQName=University of Rome&objectClsName=filas:Company&urlPage=http://ai-nlp.info.uniroma2.it/zanzotto/short_bio.html&title=Enrichment Property&op=bind
            //httpGet2("http://127.0.0.1:1979/semantic_turkey/resources/stserver/STServer?service=property&instanceQName=" + subjectInstanceName + "&propertyQName=" +predicatePropertyName+ "&objectClsName=" + objectClsName2 + "&objectQName=" + objectInstanceName +"&urlPage=" + window.arguments[0].urlPage + "&title=" + title+"&op=bind", false);
            if (op.equals("bindCreate")) {
                String rangeClsName = _oReq.getParameter(objectClsNameField);
                String lang = _oReq.getParameter(langField);
                return bindAnnotatedObjectToNewInstanceAndRelateToDroppedInstance(subjectInstanceQName, predicatePropertyName, objectInstanceNameEncoded, rangeClsName, urlPage, title, lang);
            }
            
            //httpGet2("http://127.0.0.1:1979/semantic_turkey/resources/stserver/STServer?service=property&instanceQName=University%20of%20Rome%20%22Tor%20Vergata%22&propertyQName=filas:worksInCompany&objectQName=[object%20XULElement]&lexicalization=%22Tor%20Vergata%22&urlPage=http://ai-nlp.info.uniroma2.it/zanzotto/short_bio.html&title=Enrichment%20Property&op=annot
            //httpGet2("http://127.0.0.1:1979/semantic_turkey/resources/stserver/STServer?service=property&instanceQName=" + subjectInstanceName + "&propertyQName=" +predicatePropertyName+"&objectQName=" + objectInstanceName + "&lexicalization=" + objectInstanceName +"&urlPage=" + window.arguments[0].urlPage + "&title=" + title+"&op=annot", false);
            if (op.equals("bindAnnot")) {
                String annotation = _oReq.getParameter("lexicalization");
                return addNewAnnotationForSelectedInstanceAndRelateToDroppedInstance(subjectInstanceQName, predicatePropertyName, objectInstanceNameEncoded, annotation, urlPage, title);
            }
            
            // 
        }*/ 
            
        else return ServletUtilities.getService().documentError("no handler for such a request!");
        
        this.fireServletEvent();
        return xml;
    }
    
   
    /**
     * this service returns all previous RangeAnnotations taken into the page associated to the given url
     * 
     * <Tree type="RangeAnnotations">
     *      <RangeAnnotation id="bc6da531-85d0-46d0-9118-536e31ae99fd" range="xpointer:blabla1" resource="Armando Stellato" value="Armando Stellato"/>
     *      <RangeAnnotation id="6c771bd4-9488-4efd-b5e2-66b660debbb3" range="xpointer:blabla2" resource="Marco Pennacchiotti" value="Marco Pennacchiotti"/>
     *      <RangeAnnotation id="404ccb89-5e76-4c8e-9de6-a662b84ef564" range="xpointer:blabla3" resource="Armando Stellato" value="AStellato"/>
     * </Tree>
     * 
     * @param urlPage
     * @return
     */
    public Document getPageAnnotations(String urlPage) {
        Document xml;
        ARTRepository repository = Resources.getRepository();
        
        xml = new DocumentImpl();       
        Element treeElement = xml.createElement("Tree");
        treeElement.setAttribute("type", "RangeAnnotations");
            
        ARTLiteral urlPageLiteral = repository.createLiteral(urlPage);
        Collection<ARTResource> collection = repository.getSTSubjectFromDatatypeProperty(SemAnnotVocab.Res.url, urlPageLiteral);
        
        Iterator<ARTResource> collectionIterator = collection.iterator();
        ARTResource webPage  = null;
        while (collectionIterator.hasNext()) {
            webPage = (ARTResource) collectionIterator.next();             
        }
        
        if (webPage == null) {
            xml.appendChild(treeElement);   
            return xml;
        }
        
        Predicate rangeAnnotationPredicate = ResourceOfATypePredicate.getPredicate(repository, SemAnnotVocab.Res.RangeAnnotation);
        FilterIterator filtRangeAnnotationsIt = new FilterIterator(repository.listSTSubjectInstances(SemAnnotVocab.Res.location, webPage), rangeAnnotationPredicate);

        while (filtRangeAnnotationsIt.hasNext()) {
            ARTResource semanticRangeAnnotation = (ARTResource)filtRangeAnnotationsIt.next();
            
            Iterator<ARTLiteral> textIterator = repository.listSTDatatypePropertyValues(semanticRangeAnnotation, SemAnnotVocab.Res.text);
            ARTLiteral text = textIterator.next(); //there is at least one and no more than one text for each semantic annotation
            
            Iterator<ARTLiteral> rangeIterator = repository.listSTDatatypePropertyValues(semanticRangeAnnotation, SemAnnotVocab.Res.range);
            ARTLiteral range = rangeIterator.next(); //there is at least one and no more than one range for each semantic annotation
            
            Element annotationElement = XMLHelp.newElement(treeElement,"RangeAnnotation");
            annotationElement.setAttribute("id", repository.getQName(semanticRangeAnnotation.getURI()));
            annotationElement.setAttribute("value", text.getLabel());       
            annotationElement.setAttribute("range", range.getLabel());
            ARTResource annotatedResource = repository.listSTSubjectInstances(SemAnnotVocab.Res.annotation, semanticRangeAnnotation).next(); //there is at least one and no more than one referenced resource for each semantic annotation
            annotationElement.setAttribute("resource", repository.getQName(annotatedResource.getURI()));
            
        }   
        xml.appendChild(treeElement);   
        
        return xml;
    }
    
    
    /**
     * informs the client if the requested page contains RangeAnnotations
     * 
     *  <Tree type="Ack" request="chkRangeAnnotations">
     *      <result status="yes"/>            //or "no"    
     *  </Tree>
     * 
     * @param urlPage
     * @return
     */
    public Document chkPageForAnnotations(String urlPage) {
        Document xml;
        ARTRepository repository = Resources.getRepository();
        
        xml = new DocumentImpl();       
        Element treeElement = xml.createElement("Tree");
        treeElement.setAttribute("type", "Ack");
        treeElement.setAttribute("request", "chkRangeAnnotations");
        
        ARTLiteral urlPageLiteral = repository.createLiteral(urlPage);
        Collection<ARTResource> collection = repository.getSTSubjectFromDatatypeProperty(SemAnnotVocab.Res.url, urlPageLiteral);
        
        Iterator<ARTResource> collectionIterator = collection.iterator();
        ARTResource webPage  = null;
        while (collectionIterator.hasNext()) {
            webPage = (ARTResource) collectionIterator.next();             
        }
        
        //this predicate filters out annotations which are not RangeAnnotations
        Predicate rangeAnnotationPredicate = ResourceOfATypePredicate.getPredicate(repository, SemAnnotVocab.Res.RangeAnnotation);
        FilterIterator filtRangeAnnotationsIt = new FilterIterator(repository.listSTSubjectInstances(SemAnnotVocab.Res.location, webPage), rangeAnnotationPredicate);

        Element responseElement = XMLHelp.newElement(treeElement,"result");
        
        if ( filtRangeAnnotationsIt.hasNext() )
            responseElement.setAttribute("status", "yes");
        else
            responseElement.setAttribute("status", "no");
        
        xml.appendChild(treeElement);   
        return xml;

    }
    
    
    
    /**
     * tells the server to remove an annotation; the answer informs the client of the success of this removal
     * identical implementation of its ancestor method, though a different header is returned in the response XML tree
     * 
     *  <Tree type="Ack" request="removeRangeAnnotation">
     *      <result status="yes"/>            //or "no"
     *  </Tree>
     * 
     * @param annotQName
     * @return
     */
    public Document removeAnnotation(String annotQName) {
        s_logger.debug("replying to \"removeAnnotation(" + annotQName + ")\".");
        ARTRepository repository = Resources.getRepository();
        
        ARTResource annot = repository.getSTResource(repository.expandQName(annotQName));
        
        if (annot==null)
            return servletUtilities.documentError("selected annotation is not present in the ontology");
        
        Document xml;
        
        xml = new DocumentImpl();       
        Element treeElement = xml.createElement("Tree");
        treeElement.setAttribute("type", "Ack");
        treeElement.setAttribute("request", "removeRangeAnnotation");
        
        if (deletePropertyPropagationTreeForAnnotations==null)
            initializeDeletePropertyPropagationTreeForAnnotations();
        
        Element responseElement = XMLHelp.newElement(treeElement,"result");
        
        s_logger.debug("removing annotation: " + annot);
        
        try {
            RepositoryUtilities.deepDeleteIndividual(annot, repository, deletePropertyPropagationTreeForAnnotations);
            responseElement.setAttribute("status", "yes");
        } catch (RepositoryUpdateException e) {
            e.printStackTrace();
            responseElement.setAttribute("status", "no");
        }
        
        xml.appendChild(treeElement);   
        return xml;

    }
    
    
    /**
     * creates an instance with associated annotation from the current web page
     * note that the parameter instanceQName is handled like a Qname, but it is the selection dragged from the web browser
     * @return
     */
    public Document dragDropSelectionOverClass(String instanceQName, String clsQName, String urlPage, String title, String range) {
        ServletUtilities servletUtilities = new ServletUtilities();
        s_logger.debug("dragged: " + instanceQName + " over class: " + clsQName + " on url: " + urlPage + " with title: " + title + " on range: " + range);
        String handledUrlPage = urlPage.replace(":", "%3A");
        handledUrlPage = handledUrlPage.replace("/", "%2F");
        ARTRepository repository = Resources.getRepository();    
        ARTResource instanceRes = repository.getSTResource(repository.expandQName(instanceQName));                                         
        if (instanceRes != null) {  
            return servletUtilities.documentError("there is another resource with the same name!");
        }
        try {         
            ARTResource cls = repository.getSTClass(repository.expandQName(clsQName));
            repository.addSTIndividual(repository.expandQName(instanceQName), cls);
            
            s_logger.debug("creating range annotation: " + instanceQName + " for instance: " + instanceQName + "on url: " + urlPage + " with title: " + title + " on range: " + range);
            ARTResource webPageInstance = SemanticTurkeyOperations.createWebPage(repository, urlPage, title);
            createSemanticAnnotation(repository, instanceQName, instanceQName, webPageInstance, range);
            
        } catch (RepositoryUpdateException e) {         
            s_logger.error("instance creation error", e);
            return servletUtilities.documentError("instance creation error: " + e.getMessage());
        }
        
        return updateClassOnTree(clsQName, instanceQName);
    }
    
    
    
    //TODO this is the copy of the same method present in the Cls servlet. Factorize it somehow
    /**
     * @param clsQName
     * @param instanceName
     * @return
     */
    public Document updateClassOnTree(String clsQName, String instanceName) {
        Document xml = new DocumentImpl();
        ServletUtilities servletUtilities = new ServletUtilities();
        ARTRepository repository = Resources.getRepository(); 
        Element treeElement = xml.createElement("Tree");
        treeElement.setAttribute("type", "update_cls");     
        Element clsElement = XMLHelp.newElement(treeElement,"Class");                                   
        clsElement.setAttribute("clsName", clsQName);                
        ARTResource cls = repository.getSTClass(repository.expandQName(clsQName));
        String numTotInst=""+repository.getDirectInstances(cls).size();
        clsElement.setAttribute("numTotInst",numTotInst);
        Element instanceElement = XMLHelp.newElement(treeElement,"Instance");
        instanceElement.setAttribute("instanceName", servletUtilities.decodeLabel(instanceName)); //TODO has not decode/encode been removed from our server processes?
        xml.appendChild(treeElement);   
        return xml;        
    }
    
    
    /**
     * invoked when the user drags text over an instance of the ontology. An individual for that class is created and a semantic bookmark is registered for that individual
     * in the browsed page 
     * 
     * @param subjectInstanceQName
     * @param encodedText
     * @param urlPage
     * @param title
     * @return
     */
    public Document annotateInstanceWithDragAndDrop(String subjectInstanceQName, String encodedText, String urlPage, String title, String range) {
        s_logger.debug("taking annotation for: url" + urlPage + " instanceQName: " + subjectInstanceQName + " lexicalization: " + encodedText + " title: " + title + " range: " + range);
        ServletUtilities servletUtilities = new ServletUtilities();
        ARTRepository repository = Resources.getRepository();
        try {
            s_logger.debug("creating range annotation: " + encodedText + " for instance: " + subjectInstanceQName + "on url: " + urlPage + " with title: " + title + " on range: " + range);
            ARTResource webPageInstance = SemanticTurkeyOperations.createWebPage(repository, urlPage, title);
            createSemanticAnnotation(repository, subjectInstanceQName, encodedText, webPageInstance, range);
        } catch (RepositoryUpdateException e) {
            s_logger.error("range annotation creation error: ", e);
            return servletUtilities.documentError("range annotation creation error: " + e.getMessage());
        }           
        
        Document xml = new DocumentImpl();
        Element tree = xml.createElement("Tree");
        tree.setAttribute("type","Ack");
        xml.appendChild(tree);
        
        return xml; 
    }
    
    
    /**Metodo che crea una istanza di SemanticAnnotation associando una lexicalization all'istanza della classe  
     *@param SesameARTRepositoryImpl repository
     *@param String instanceName
     *@param String lexicalization
     *@param String webPageInstanceName
     * @throws RepositoryUpdateException 
     **/
    private void createSemanticAnnotation(ARTRepository repository, String individualQName, String text, ARTResource webPageInstanceRes, String range) throws RepositoryUpdateException {        
                
        String semanticAnnotationID = SemanticTurkeyOperations.generateNewSemanticAnnotationUUID(repository);
                                
        repository.addSTIndividual(  repository.getDefaultNamespace() + semanticAnnotationID,  SemAnnotVocab.Res.RangeAnnotation );          
            
        ARTResource semanticAnnotationInstanceRes = repository.getSTResource(repository.getDefaultNamespace() + semanticAnnotationID);      
        //s_logger.debug("creating range annotation: semAnnotInstanceRes: " + semanticAnnotationInstanceRes + "");
        repository.instanciateDatatypeProperty(semanticAnnotationInstanceRes, SemAnnotVocab.Res.text, text);                              
        repository.instanciateDatatypeProperty(semanticAnnotationInstanceRes, SemAnnotVocab.Res.range, range);
        
        repository.instanciateObjectProperty(semanticAnnotationInstanceRes, SemAnnotVocab.Res.location, webPageInstanceRes);        
        
        ARTResource instanceRes = repository.getSTResource(repository.expandQName(individualQName));
        repository.instanciateObjectProperty(instanceRes, SemAnnotVocab.Res.annotation, semanticAnnotationInstanceRes);
    }
    
    
    public Document saveAnnotations(File file) {
        ARTRepository repository = Resources.getRepository();
        
        Document xml;
        xml = new DocumentImpl();       
        Element rootElement = xml.createElement("SemanticAnnotations");
        rootElement.setAttribute("type", "RangeAnnotations");
        
        Iterator<ARTResource> itr = repository.listSTSubjectInstances(SemAnnotVocab.Res.url, null);
        HashSet<ARTResource> webpages = new HashSet<ARTResource>();
        while (itr.hasNext())
            webpages.add(itr.next());
        Iterator<ARTResource> webPagesIterator = webpages.iterator();
        ARTResource webPage  = null;
        while (webPagesIterator.hasNext()) {
            webPage = webPagesIterator.next();             
            Predicate rangeAnnotationPredicate = ResourceOfATypePredicate.getPredicate(repository, SemAnnotVocab.Res.RangeAnnotation);
            FilterIterator filtRangeAnnotationsIt = new FilterIterator(repository.listSTSubjectInstances(SemAnnotVocab.Res.location, webPage), rangeAnnotationPredicate);

            Element webPageElement = XMLHelp.newElement(rootElement,"WebPage");
            String url = repository.listSTDatatypePropertyValues(webPage, SemAnnotVocab.Res.url).next().getLabel();
            webPageElement.setAttribute("url", url);            
            
            while (filtRangeAnnotationsIt.hasNext()) {
                ARTResource semanticRangeAnnotation = (ARTResource)filtRangeAnnotationsIt.next();
                
                Iterator<ARTLiteral> textIterator = repository.listSTDatatypePropertyValues(semanticRangeAnnotation, SemAnnotVocab.Res.text);
                ARTLiteral text = textIterator.next(); //there is at least one and no more than one text for each semantic annotation
                
                Iterator<ARTLiteral> rangeIterator = repository.listSTDatatypePropertyValues(semanticRangeAnnotation, SemAnnotVocab.Res.range);
                ARTLiteral range = rangeIterator.next(); //there is at least one and no more than one range for each semantic annotation
                
                Element annotationElement = XMLHelp.newElement(webPageElement,"RangeAnnotation");
                annotationElement.setAttribute("id", repository.getQName(semanticRangeAnnotation.getURI()));
                annotationElement.setAttribute("value", text.getLabel());       
                annotationElement.setAttribute("range", range.getLabel());
                ARTResource annotatedResource = repository.listSTSubjectInstances(SemAnnotVocab.Res.annotation, semanticRangeAnnotation).next(); //there is at least one and no more than one referenced resource for each semantic annotation
                annotationElement.setAttribute("resource", repository.getQName(annotatedResource.getURI()));
                ARTResource type = repository.listSTObjectPropertyValues(annotatedResource, RDF.Res.TYPE ).next().asResource();
                annotationElement.setAttribute("type", type.getURI());                
            }
        }
        
        xml.appendChild(rootElement);

        try {
            BufferedWriter out = new BufferedWriter(new FileWriter(file));
            out.write(XMLHelp.XML2String(xml, true));
            out.close();
            System.out.println(xml);
        } catch (IOException e) {
            return servletUtilities.documentError(e.getMessage());
        }
        
        xml = new DocumentImpl();       
        Element treeElement = xml.createElement("Tree");
        treeElement.setAttribute("type", "Ack");
        treeElement.setAttribute("request", "saveAnnotations");
        Element responseElement = XMLHelp.newElement(treeElement,"result");
        responseElement.setAttribute("status", "yes");
        xml.appendChild(treeElement);
        return xml;
        
    }

    
    
    public CXSL CXSLFactory() {
        // TODO Auto-generated method stub
        return null;
    }

    
}
